package com.example.ormsqlsplicingframe1.orm.Util;

import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;

import java.beans.Introspector;
import java.lang.invoke.SerializedLambda;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class ReflectionUtil {

    private static Map<SerializableFunction<?, ?>, Field> cache = new ConcurrentHashMap<>();

    public static <T, R> String getFieldName(SerializableFunction<T, R> function) {
        Field field = ReflectionUtil.getField(function);
        String name = field.getName();
        return formatStr(name);
    }

    public static <T, R> String getFieldNameAs(SerializableFunction<T, R> function) {
        Field field = ReflectionUtil.getField(function);
        String name = field.getName();
        return formatStr(name) + " as " + name;
    }


    public static Field getField(SerializableFunction<?, ?> function) {
        return cache.computeIfAbsent(function, ReflectionUtil::findField);
    }

    public static Field findField(SerializableFunction<?, ?> function) {
        Field field = null;
        String fieldName = null;
        try {
            // 第1步 获取SerializedLambda
            Method method = function.getClass().getDeclaredMethod("writeReplace");
            method.setAccessible(Boolean.TRUE);
            SerializedLambda serializedLambda = (SerializedLambda) method.invoke(function);
            // 第2步 implMethodName 即为Field对应的Getter方法名
            String implMethodName = serializedLambda.getImplMethodName();
            if (implMethodName.startsWith("get") && implMethodName.length() > 3) {
                fieldName = Introspector.decapitalize(implMethodName.substring(3));

            } else if (implMethodName.startsWith("is") && implMethodName.length() > 2) {
                fieldName = Introspector.decapitalize(implMethodName.substring(2));
            }
            // 第3步 获取的Class是字符串，并且包名是“/”分割，需要替换成“.”，才能获取到对应的Class对象
            String declaredClass = serializedLambda.getImplClass().replace("/", ".");
            Class<?> aClass = Class.forName(declaredClass, false, ClassUtils.getDefaultClassLoader());

            // 第4步  Spring 中的反射工具类获取Class中定义的Field
            field = ReflectionUtils.findField(aClass, fieldName);

        } catch (Exception e) {
            e.printStackTrace();
        }
        // 第5步 如果没有找到对应的字段应该抛出异常
        if (field != null) {
            return field;
        }
        throw new NoSuchFieldError(fieldName);
    }

    public static HashMap<String, Object> object2map(Object o, String classpath) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
        HashMap<String, Object> json = new HashMap<>();
        if (o == null) {
            o = Class.forName(classpath).newInstance();
        }
        for (Field declaredField : o.getClass().getDeclaredFields()) {
            String type = declaredField.getGenericType().toString();
            declaredField.setAccessible(true);
            if (type.indexOf("java.lang") != -1) {
//                System.out.println(type.substring(type.indexOf("java.lang") + 10, type.length()));

                try {
                    json.put(declaredField.getName(), declaredField.get(o));
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            } else {
                String newclasspath = type.substring(type.indexOf("class") + 6, type.length());
                json.put("class:" + declaredField.getName(), object2map(declaredField.get(o), newclasspath));
                ;
            }
        }
        return json;
    }

    public static HashMap<String, Object> object2mapisnull(Object o) throws IllegalAccessException {
        HashMap<String, Object> json = new HashMap<>();
        if (o == null) {
            return null;
        }
        for (Field declaredField : o.getClass().getDeclaredFields()) {
            String type = declaredField.getGenericType().toString();
            declaredField.setAccessible(true);
            if (type.indexOf("java.lang") != -1) {
//                System.out.println(type.substring(type.indexOf("java.lang") + 10, type.length()));

                if (declaredField.get(o) != null) {
                    json.put(declaredField.getName(), declaredField.get(o));
                }
            } else if (type.indexOf("java.util.List") != -1) {

                ArrayList<HashMap<String, Object>> lists = new ArrayList<>();
                List oldlist = (List) declaredField.get(o);
                for (Object o1 : oldlist) {
                    lists.add(object2mapisnull(o1));
                }

                json.put("java.util.List" + declaredField.getName(), lists);

            } else {
                HashMap<String, Object> omap = object2mapisnull(declaredField.get(o));
                if (omap != null) {
                    json.put("class:" + declaredField.getName(), omap);
                }
            }
        }
        return json;
    }


    public static String formatStr(String key) {
        StringBuffer key1 = new StringBuffer();
        int t = 0;
        for (char c : key.toCharArray()) {
            if (c > 90 || c > '0' && c < '9') {
                key1.append(c);
            } else {
                if (t == 0) {
                    key1.append((char) (c + 32));
                    continue;
                }
                key1.append("_").append((char) (c + 32));
            }
            t++;
        }
        return key1.toString();
    }

}
